#region using

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Runtime.InteropServices;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Xml.Serialization;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.WebPartPages;
using PS.Tools.Utils;
using PS.WebParts.Rollup.GridViewWebPart;

#endregion

namespace PS.WebParts
{
    internal class TimeSpanEx
    {
        public TimeSpan Time;
    }

    internal class TimeChecker
        : IDisposable
    {
        private DateTime StartDT;
        private TimeSpanEx Result;

        public TimeChecker(TimeSpanEx resultTime)
        {
            Result = resultTime;
            StartDT = DateTime.Now;
        }

        public void Dispose()
        {
            Result.Time = DateTime.Now - StartDT;
        }
    }

    [
        Guid("2E0EE161-068F-45c7-AF5C-29F312E8719E"),
        XmlRoot(Namespace = "PS.GridViewWebPart"),
        ParseChildren(ChildrenAsProperties = false),
        PersistChildren(false)
    ]
    public class GridViewWebPart
        : DataWebPartBase
    {
        private const int DefTimeout = 1000;
        private const int DefPageSize = 10;



        private string columnString = Constants.ColumnsScheme;

        private SPGridView grid;
        private ObjectDataSource dataSourceWrapper;
        private Exception exception = null;

        private int pageSize = DefPageSize;
        private int timeoutToDisplay = DefTimeout;

        private DataAdapter adapter;
        private UpdatePanel panel;
        private bool debugMode = false;
        
        private TimeSpanEx getDataTime = new TimeSpanEx();

        private static readonly bool EnableAjax = true;        
        private static readonly string FilterExprName = "FilterExpression";
        

        #region public Properties

        [
            DefaultValue(DefPageSize),
            Browsable(true),
            WebPartStorage(Storage.Shared),
            Category("Behaviour"),
            FriendlyName("Page Size")
        ]
        public int PageSize
        {
            get
            {
                return pageSize;
            }
            set
            {
                if (value > 0)
                    pageSize = value;
                else
                    throw new Exception("Page Size <=0 ");
            }

        }

        [
            DefaultValue(false),
            Browsable(true),
            WebPartStorage(Storage.Shared),
            Category("Behaviour"),
            FriendlyName("Debug mode")
        ]
        public bool DebugMode
        {
            get
            {
                return debugMode;
            }
            set
            {
                debugMode = value;
            }

        }

        
        

        [
            DefaultValue(DefTimeout),
            Browsable(true),
            WebPartStorage(Storage.Shared),
            Category("Behaviour"),
            FriendlyName("Timeout to Display in  Milliseconds")
        ] 
        
        public int TimeoutToDisplay
        {
            set
            {
                timeoutToDisplay = value;   
            }
            get
            {
                return timeoutToDisplay ;   
            }


        }



        [
            WebPartStorage(Storage.Shared),
            DefaultValue(Constants.ColumnsScheme),
            Browsable(false)
        ]
        public string ColumnString
        {
            get { return columnString; }
            set { columnString = value; }
        }

        private bool? firstLoad;
        protected bool FirstLoad
        {
            get
            {
                if (!firstLoad.HasValue)
                    firstLoad=ViewState["NotFirstStart"] == null;
                return firstLoad.Value;
            }
            set
            {
                ViewState["NotFirstStart"] = value;
            }
        }

        #endregion

        private bool NeedDelayLoad
        {
            get
            {
                return TimeoutToDisplay > 0;
            }
        }

        private DataAdapter Data
        {
            get
            {
                if (null == adapter)
                {
                    IDataSource dataSource;

                    if (DataSources.Count > 0)
                    {
                        dataSource = DataSources[0];
                    }
                    else
                    {
                        throw new Exception("No Data Source");
                    }

                    adapter = new DataAdapter(dataSource);
                }
                return adapter;
            }
        }

        private void SaveColumnScheme(DataControlFieldCollection columns)
        {
            ColumnCollection collection = new ColumnCollection();

            foreach (BoundField field in columns)
            {
                collection.Columns.Add(field);
            }
            ColumnString = XmlSerializationHelper<ColumnCollection>.Serialize(collection);
        }

        private void RestoreColumns(DataControlFieldCollection columns)
        {
            ColumnCollection collection =
                string.IsNullOrEmpty(ColumnString)
                        ? new ColumnCollection()
                        : XmlSerializationHelper<ColumnCollection>.Deserialize(ColumnString);

            columns.Clear();

            if (0 == collection.Columns.Count)
            {
                GenerateColumns(columns);
            }
            else
            {
                foreach (BoundField field in collection.Columns)
                {
                    columns.Add(field);
                }
            }
        }

        public override ToolPart[] GetToolParts()
        {
            List<ToolPart> toolParts = new List<ToolPart>(base.GetToolParts());

            GridViewToolPart toolpart = new GridViewToolPart(this);
            toolParts.Insert(0, toolpart);

            return toolParts.ToArray();
        }

        #region private class TableParameter

        private class TableParameter
            : Parameter
        {
            public DataTable table;

            public TableParameter(string name, DataTable table)
                : base(name)
            {
                this.table = table;
            }
            protected override object Evaluate(HttpContext context, Control control)
            {
                return table;
            }
        }

        #endregion

        

        protected override void Render(HtmlTextWriter writer)
        {
            if (null != exception)
            {
                if (debugMode)
                {
                    writer.Write("<font color=red>Exception</font>: {0} ", exception.ToString());
                }
                else
                {
                    writer.Write("<font color=grey>Generic error. Please switch to debug mode.</font>");
                }
            }
            else
            {
                TimeSpanEx renderTime = new TimeSpanEx();

                using (new TimeChecker(renderTime))
                {
                    try
                    {
                        base.Render(writer);
                    }
                    catch (Exception ex)
                    {
                        exception = ex;
                    }
                }

                if (DebugMode)
                {
                    writer.Write("<font color=blue>Query time(ms): " + getDataTime.Time.TotalMilliseconds + "<br/>" +
                                 "<font color=blue>Render time(ms): " + renderTime.Time.TotalMilliseconds);
                }
            }
        }

        

        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);
            try
            {
                if (EnableAjax)
                {

                    if (FirstLoad && NeedDelayLoad)
                    {
                        string updateScript = Page.ClientScript.GetPostBackEventReference(panel, "customPostback");
                        string startupScript = string.Format(
                            @"if (_spBodyOnLoadFunctionNames){{_spBodyOnLoadFunctionNames.push(""setTimeout(\""{0}\"", {1})"");}}"
                            , updateScript, TimeoutToDisplay);
                        Page.ClientScript.RegisterStartupScript(GetType(), "startupKey", startupScript, true);
                    }

                    else
                    {
                        CreateAndConfigureControls();
                    }

                }

                if (null != dataSourceWrapper)
                {
                    ViewState[FilterExprName] = dataSourceWrapper.FilterExpression;
                }

                FirstLoad = false;
                //grid.HeaderRow.Visible = true;
            } catch( Exception ex)
            {
                exception = ex;
            }
        }

        protected void CreateDataSource()
        {
            DataTable table;

            if (FirstLoad && NeedDelayLoad )
            {
                table = Data.GetSchema();
            }
            else
            {
                using (new TimeChecker(getDataTime))
                {
                    table = Data.GetData();
                }
            }

            dataSourceWrapper = new ObjectDataSource(typeof(DataTableWrapper).AssemblyQualifiedName, "GetView");
            dataSourceWrapper.ID = dataSourceWrapper.ID;

            dataSourceWrapper.SelectParameters.Add(new TableParameter("Table", table));

            Controls.Add(dataSourceWrapper);
        }

        private void GenerateColumns(DataControlFieldCollection columns)
        {
            Data.FillColumn(columns);
            SaveColumnScheme(columns);
        }

        private static string CreateFilteredColumns(DataControlFieldCollection columns)
        {
            string filterDataFields = string.Empty;

            string delimeter = string.Empty;
            foreach (BoundField field in columns)
            {
                filterDataFields += delimeter + field.DataField;
                delimeter = ",";
            }
            return filterDataFields;
        }

        internal void GenerateColumns()
        {
            try
            {
                grid.Columns.Clear();
                GenerateColumns(grid.Columns);
            } catch(Exception ex)
            {
                exception = ex;
            }
        }

        private void PreConfigureGrid()
        {
            grid.ID = "grid";
            grid.AutoGenerateColumns = false;
            grid.AllowSorting = true;
            grid.PageSize = PageSize;
            grid.AllowPaging = true;
            grid.AllowFiltering = true;
            grid.DataSourceID = dataSourceWrapper.ID;
            grid.FilteredDataSourcePropertyName = FilterExprName;
            grid.FilteredDataSourcePropertyFormat = "{1} LIKE '{0}'";

            //grid.EmptyDataTemplate = null;
            //grid.EmptyDataText = null;
            
            
        }

        private void AfterControlsAdd()
        {
            grid.PagerTemplate = null;
            if (null != ViewState[FilterExprName])
            {
                dataSourceWrapper.FilterExpression = ViewState[FilterExprName].ToString();
            }
        }

        private void CreateAndConfigureControls()
        {
            try
            {
                RestoreColumns(grid.Columns);
                grid.FilterDataFields = CreateFilteredColumns(grid.Columns);
                grid.DataBind();

            }
            catch (Exception ex)
            {
                exception = ex;
            }
        }

        protected override void CreateChildControls()
        {
            base.CreateChildControls();
            try
            {
                grid = new PSGridView();
                panel = new UpdatePanel();
                
                Literal loadingImage = new Literal();
                loadingImage.Text = "<center><img src='~/_layouts/images/loading6.gif'/></center>";
                CreateDataSource();
                PreConfigureGrid();


                if (EnableAjax)
                {
                    Controls.Add(panel);
                    panel.ContentTemplateContainer.Controls.Add(grid);
                    if (FirstLoad && NeedDelayLoad )
                    {
                        panel.ContentTemplateContainer.Controls.Add(loadingImage);
                    }
                }
                else
                {
                    Controls.Add(grid);
                }
                AfterControlsAdd();
            }
            catch (Exception ex)
            {
                exception = ex;
            }
        }
    }
}
